(function(angular) {
	'use strict';
	angular.module("fs.waf").factory('formatService', [ '$filter', 'storageService', 'SYS_PARAMS',  function($filter, storageService, SYS_PARAMS) {
		var defaultFormat = {
			number : '#0.0##############',
			money : '#,##0.00#',
			percentage : '#0.0#'
		};
		function getMaximumIntegerDigits(pattern) {
			if (!pattern)
				return 1;
			if (pattern.indexOf('#') == 0) {
				return 2147483647;
			}
			pattern.replace('[,]', '');
			return pattern.length;
		}
		function getMaximumFractionDigits(pattern) {
			if (!pattern)
				return 0;
			return pattern.length;
		}
		function round(number, decimal) {
			if(isNaN(number)) return '';
			if (decimal < 0)
				return new Big(number).toString();
			return new Big(number).round(decimal).toString();
		}

		function formatNumber(number, pattern) {
			var maximumIntegerDigits, maximumFractionDigits, formatInfo, formatOptions, numberArray, numberString;
			var formatArray, formatString, length, len, i, result = '', sign = '';
			if (number === null || angular.isUndefined(number) || (angular.isString(number) ? number.trim() == '' : false)) {
				return;
			}
			formatInfo = storageService.getFormatInfo() || {};
			pattern = formatInfo[pattern] || pattern;
			pattern = pattern || formatInfo.defaultFloat || defaultFormat.number;
			if (!/.*[#0]+.*/.test(pattern.replace(/[^#0,\.]*/, ''))) {
				return number;
			}

			formatOptions = {
				decimalSeparator : formatInfo.decimalSeparator || '.',
				groupSeparator : formatInfo.groupSeparator || ',',
				groupingUsed : pattern.indexOf(',') >= 0,
				groupingSize : ((pattern.indexOf('.') || pattern.length - 1) - pattern.indexOf(',') - 1)
			};

			formatArray = pattern.split('.');
			maximumIntegerDigits = getMaximumIntegerDigits(formatArray[0]);
			maximumFractionDigits = getMaximumFractionDigits(formatArray.length > 1 ? formatArray[1] : '');
			if(isNaN(number)) return '';
			number = round(number, maximumFractionDigits);
			numberString = '' + number;
			numberArray = numberString.split('.');
			if (numberArray[0].length > maximumIntegerDigits) {
				numberArray[0] = numberArray[0].substr(numberArray[0].length - maximumIntegerDigits);
				numberString = numberArray.join('.');
			}

			numberArray = numberString.split(".");
			numberString = numberArray[0];
			formatString = formatArray[0];
			len = numberArray[0].length - 1;
			for (i = formatString.length - 1; i >= 0; i--) {
				switch (formatString.substr(i, 1)) {
				case '#':
					if (len >= 0)
						result = numberString.substr(len--, 1) + result;
					break;
				case '0':
					if (len >= 0)
						result = numberString.substr(len--, 1) + result;
					else
						result = '0' + result;
					break;
				case ',':
					result = formatOptions.groupSeparator + result;
					break;
				}
			}
			if (len >= 0) {
				if (formatOptions.groupingUsed) {
					length = numberArray[0].length;
					for (; len >= 0; len--) {
						result = numberArray[0].substr(len, 1) + result;
						if (len > 0 && ((length - len) % formatOptions.groupingSize) == 0)
							result = formatOptions.groupSeparator + result;
					}
				} else
					result = numberString.substr(0, len + 1) + result;
			}
			result += formatOptions.decimalSeparator;
			numberString = numberArray.length > 1 ? numberArray[1] : '';
			formatString = formatArray.length > 1 ? formatArray[1] : '';
			len = 0;
			for (var i = 0; i < formatString.length; i++) {
				switch (formatString.substr(i, 1)) {
				case '#':
					if (len < numberString.length)
						result += numberString.substr(len++, 1);
					break;
				case '0':
					if (len < numberString.length)
						result += numberString.substr(len++, 1);
					else
						result += '0';
					break;
				}
			}
			if (result.charAt(0) == '-' || result.charAt(0) == '+') {
				sign = result.charAt(0);
				result = result.substring(1);
			}
			result = sign + result.replace(/^\,/, '').replace(/\.$/, '');
			return result;
		}
		function formatMoney(money, pattern) {// pattern is ccy or ccy format
			// TODU: need to get ccyConfig from backend
			var formatInfo, siteInfo, ccyConfig = SYS_PARAMS.CCY_CONFIG, zeros = ".000000000000000000000000000000000000000000000000", fractionDigits;
			formatInfo = storageService.getFormatInfo() || {};
			siteInfo = storageService.getSiteInfo() || {};
			if(ccyConfig[angular.uppercase(pattern)] == undefined){
				fractionDigits = ccyConfig[siteInfo.baseCcy];
			}else{
				fractionDigits = ccyConfig[angular.uppercase(pattern)];
			}
			if (!(money === null || angular.isUndefined(money) || (angular.isString(money) ? money.trim() == '' : false))) {
				pattern = (formatInfo.money0 || '#,##0') + zeros.substr(0, fractionDigits + 1);
			} else {
				pattern = formatInfo[pattern];
				pattern = pattern || formatInfo['defaultMoney'];
			}
			return formatNumber(money, pattern);
		}
		function formatSst(sensitiveInfo, pattern) {
			return $filter("sensitive")(sensitiveInfo, pattern.positiveOffset, pattern.negativeOffset, pattern.mask);
		}
		function formatPercentage(number, pattern) {
			var formatInfo = storageService.getFormatInfo() || {};
			pattern = formatInfo[pattern];
			pattern = pattern || defaultFormat.percentage;
			number = formatNumber(number * 100, pattern);
			return number = number ? number + '%' : number;
		}
		function formatDate(date, pattern) {
			var formatInfo = storageService.getFormatInfo() || {};
			pattern = formatInfo[pattern] || pattern;
			return $filter('dateFormat')(date, pattern);
		}
		function parseNumber(number) {
			var formatInfo, decimalSeparator, groupSeparator;
			if (angular.isNumber(number))
				return number;
			if (!angular.isString(number)) {
				return number;
			}
			if (number === null || angular.isUndefined(number) || (angular.isString(number) ? number.trim() == '' : false)) {
				return '';
			}
			formatInfo = storageService.getFormatInfo() || {};
			decimalSeparator = formatInfo.decimalSeparator || '.';
			groupSeparator = formatInfo.groupSeparator || ',';
			number = number.replace(eval("/\\" + groupSeparator + "/g"), "");
			number = number.replace(eval("/\\" + decimalSeparator + "/g"), ".");
			return isNaN(number) ? '' : new Big(number).toString();
		}
		function parseMoney(money) {
			return parseNumber(money);
		}
		function parsePercentage(percentage) {
			if(!percentage || percentage == ''){
				return percentage;
			}
			if (angular.isNumber(percentage))
				return percentage / 100;
			return parseNumber(percentage.replace(/%$/g, '')) / 100;
		}
		function parseDate(date, pattern) {
			var formatInfo, i_val = 0, i_format = 0, c = "", token = "", token2 = "", x, y, year = new Date().getFullYear();
			var month = 1, day = 1, hh = 0, mm = 0, ss = 0, sss = 0, ampm = "", zs = "", zh = 0, zm = 0;
			if(!date || !pattern)
			return date;
			formatInfo = storageService.getFormatInfo() || {};
			pattern = formatInfo[pattern] || pattern;
			// check the value is 0 - 9.
			this.isInteger = function(val) {
				for (var i = 0; i < val.length; i++) {
					if ("1234567890".indexOf(val.charAt(i)) == -1) {
						return false;
					}
				}
				return true;
			};
			// Gets the token from minlength to maxlength.
			this.getInt = function(str, i, minlength, maxlength) {
				for (var x = maxlength; x >= minlength; x--) {
					var token = str.substring(i, i + x);
					if (token.length < minlength) {
						return null;
					}
					if (this.isInteger(token)) {
						return token;
					}
				}
				return null;
			};

			this.buildDate = function(year, month, day, hh, mm, ss, sss, zh, zm, zs) {
				var date = new Date(0);
				var dateSetter = date.setFullYear;//zs != '' ? date.setUTCFullYear : date.setFullYear;
				var timeSetter = date.setHours;//zs != '' ? date.setUTCHours : date.setHours;
				var h = (hh || 0); //- zh;
				var m = (mm || 0); //- zm;
				var s = (ss || 0);
				var ms = Math.round(parseFloat('0.' + (sss || 0)) * 1000);
				dateSetter.call(date, year, month - 1, day);
				timeSetter.call(date, h, m, s, ms);
				return date;
			};

			while (i_format < pattern.length) {
				c = pattern.charAt(i_format);
				token = "";
				while ((pattern.charAt(i_format) == c) && (i_format < pattern.length)) {
					token += pattern.charAt(i_format++);
				}
				if (token == "yyyy" || token == "yy" || token == "y") {
					if (token == "yyyy") {
						x = 4;
						y = 4;
					}
					if (token == "yy") {
						x = 2;
						y = 2;
					}
					if (token == "y") {
						x = 2;
						y = 4;
					}
					year = this.getInt(date, i_val, x, y);
					if (year == null)
						return date;
					i_val += year.length;
				} else if (token == "MM" || token == "M") {
					month = this.getInt(date, i_val, token.length, 2);
					if (month == null || month < 1 || month > 12)
						return date;
					i_val += month.length;
				} else if (token == "dd" || token == "d") {
					day = this.getInt(date, i_val, token.length, 2);
					if (day == null || (day < 1) || (day > 31))
						return date;
					i_val += day.length;
				} else if (token == "hh" || token == "h") {
					hh = this.getInt(date, i_val, token.length, 2);
					if (hh == null || (hh < 1) || (hh > 12))
						return date;
					i_val += hh.length;
				} else if (token == "HH" || token == "H") {
					hh = this.getInt(date, i_val, token.length, 2);
					if (hh == null || (hh < 0) || (hh > 23))
						return date;
					i_val += hh.length;
				} else if (token == "mm" || token == "m") {
					mm = this.getInt(date, i_val, token.length, 2);
					if (mm == null || (mm < 0) || (mm > 59))
						return null;
					i_val += mm.length;
				} else if (token == "ss" || token == "s") {
					ss = this.getInt(date, i_val, token.length, 2);
					if (ss == null || (ss < 0) || (ss > 59))
						return date;
					i_val += ss.length;
				} else if (token == "sss" || token == "SSS" || token == "S") {
					sss = this.getInt(date, i_val, token.length, 3);
					if (sss == null || (sss < 0) || (sss > 999))
						return date;
					i_val += sss.length;
				} else if (token == "Z" || token == "z") {
					zs = date.substring(i_val, i_val + 1);
					zh = this.getInt(date, i_val + 1, 2, 2);
					zm = this.getInt(date, i_val + 3, 2, 2);
					zh = zs == '+' ? zh : -zh;
					zm = zs == '+' ? zm : -zm;
					i_val += 5;
				} else if (token == "a") {
					if (date.substring(i_val, i_val + 2).toLowerCase() == "am") {
						ampm = "AM";
					} else if (date.substring(i_val, i_val + 2).toLowerCase() == "pm") {
						ampm = "PM";
					} else {
						return date;
					}
					i_val += 2;
				} else {
					if (date.substring(i_val, i_val + token.length) != token) {
						return date;
					} else {
						i_val += token.length;
					}
				}
			}
			// validate data
			if (i_val != date.length)
				return date;
			// validate month
			if (month == 2) {
				if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
					if (day > 29)
						return date;
				} else {
					if (day > 28)
						return date;
				}
			}
			if ((month == 4) || (month == 6) || (month == 9) || (month == 11)) {
				if (day > 30)
					return date;
			}
			// set ampm
			if (hh < 12 && ampm == "PM") {
				hh = hh - 0 + 12;
			} else if (hh > 11 && ampm == "AM") {
				hh -= 12;
			}
			return this.buildDate(year, month, day, hh, mm, ss, sss, zh, zm, zs);
		}
		return {
			formatNumber : formatNumber,
			formatMoney : formatMoney,
			formatPercentage : formatPercentage,
			formatDate : formatDate,
			formatSst : formatSst,
			parseNumber : parseNumber,
			parseMoney : parseMoney,
			parsePercentage : parsePercentage,
			parseDate : parseDate,
			round : round
		};
	} ]);
})(angular);
